#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "dict.h"
#include "xmalloc.h"


dict_t *
create_dict(bool malloc_kv_ptr)
{
  dict_t *dict = ncmalloc(1, sizeof(struct dict));
  if (!dict) return (NULL);
  dict->elems = (struct array *) array_create(sizeof(struct dict_elem), DICT_INITSIZ);
  if (!dict->elems) return (NULL);
  dict->__make_copy = malloc_kv_ptr;
  return (dict);
}

void
free_dict(dict_t *dict)
{
  size_t i;
  dict_elem_t *e = NULL;

  if (!dict) return;
  if (dict->elems && dict->__make_copy)
  {
    for (i = 0; i < dict->elems->used; i++)
    {
      e = (dict_elem_t *) array_get_offset(dict->elems, i);
      ncfree(e->key, (strlen(e->key)+1)*sizeof(char));
      ncfree(e->value, (strlen(e->value)+1)*sizeof(char));
    }
  }

  array_free(dict->elems);
  ncfree(dict, sizeof(dict_t));
}

size_t
dict_len(dict_t *dict)
{
  return (dict->elems->used);
}

dict_elem_t *
get_dict_elem(dict_t *dict, size_t idx)
{
  if (idx < dict->elems->used) {
    return ((dict_elem_t *) array_get_offset(dict->elems, idx));
  } else {
    return (NULL);
  }
}

static long int
get_key_idx(const dict_t *dict, const char *key)
{
  size_t i;
  dict_elem_t *e = NULL;

  for (i = 0; i < dict->elems->used; i++)
  {
    e  = (dict_elem_t *) array_get_offset(dict->elems, i);
    if (e->key && e->value)
    {
      if ( (strlen((char *) key) == strlen(e->key)) && (strcmp(key, e->key) == 0) )
      {
        return (i);
      }
    }
  }

  return -1;
}

long int
insert(dict_t *dict, const char *key, size_t klen, const char *value, size_t vlen)
{
  long int idx;
  dict_elem_t *e = NULL;

  if (!dict || !dict->elems || !key || !value || klen <= 0 || vlen <= 0) return (-1);

  if ( (idx = get_key_idx(dict, key)) == -1 )
  {
    e = (dict_elem_t *)ncmalloc(1, sizeof(struct dict_elem));
    if (dict->__make_copy) {
      e->key = (char *) ncmalloc(klen+1, sizeof(char));
      strncpy(e->key, key, klen);
      e->key[klen] = '\0';
      e->value = (char *) ncmalloc(vlen+1, sizeof(char));
      strncpy(e->value, value, vlen);
      e->value[vlen] = '\0';
    } else {
      e->key = (char *) key;
      e->value = (char *) value;
    }
    array_add_data(dict->elems, (void *)e, 1);
    ncfree(e, sizeof(struct dict_elem));
    return (dict->elems->used);
  }

  return (idx);
}

long int
insert_s(dict_t *dict, const char *key, const char *value)
{
  return ( insert(dict, key, strlen(key), value, strlen(value)) );
}

long int
insert_str(dict_t *dict, string_t *key, string_t *value)
{
  return ( insert(dict, get_string(key), get_strlen(key), get_string(value), get_strlen(value)) );
}

long int
insert_f(dict_t *dict, const char *key, const char *fmt, ...)
{
  va_list ap;
  char *s = NULL;
  int ret;

  va_start(ap, fmt);
  vasprintf(&s, fmt, ap);
  va_end(ap);
  ret = insert_s(dict, key, s);
  free(s);
  return (ret);
}

const char *
lookup(dict_t *dict, const char *key)
{
  dict_elem_t *e = NULL;
  long int idx;

  if (!dict || !dict->elems || !key) return (NULL);
  if ( (idx = get_key_idx(dict, key)) < 0 ) return (NULL);
  e = get_dict_elem(dict, idx);
  if (!e || !e->value) return (NULL);
  return (e->value);
}

#ifdef ENABLE_DEBUG
void
print_dict_debug(dict_t *dict)
{
  size_t i;
  dict_elem_t *e = NULL;

  fprintf(stderr, "[dict usage]\n");
  if (!dict || !dict->elems)
  {
    fprintf(stderr, "  PTR/MEM Error: dict ptr is null\n");
  }
  fprintf(stderr, "  content: ");
  for (i = 0; i < dict->elems->used; i++)
  {
    e = get_dict_elem(dict, i);
    fprintf(stderr, "[%s|%s] ", e->key, e->value);
  }
  if (dict->elems->used == 0)
  {
    fprintf(stderr, "empty.");
  }
  fprintf(stderr, "\n");
}
#endif

